<!DOCTYPE HTML>
<html>
<head>
  <meta charset="utf-8">
  <title>日历控件（基础版）</title>
  <style>
    /**
     + ---------------------------------------- +
     + 日历控件 Css v1.0
     + By: Ferris
     + QQ: 21314130
     + Mail: fgm@fgm.cc, Angtian.fgm@taobao.com
     + ---------------------------------------- +
     + Date: 2011-12-18
     + ---------------------------------------- +
    **/
    .calendar{position:relative;float:left;border:1px solid;border-color:#85BEE5 #3485C0 #3485C0 #85BEE5;padding:0 30px 15px;background:#FFF;}
    .cal-container{float:left;margin:10px 10px 0;}
    .calendar dl{width:183px;color:#404040;font:12px/22px Tahoma;margin:10px 10px 0;}
    .calendar dl,.calendar dt,.calendar dd{text-align:center;margin:0;padding:0;}
    .calendar dt{float:left;width:25px;height:22px;margin-left:1px;}
    .calendar dt.date{width:100%;font-weight:700;border-bottom:1px solid #E4E4E4;margin:0;}
    .calendar dd{clear:both;width:183px;height:139px;font-weight:700;background:url(bg.png) no-repeat;}
    .calendar dd a,.calendar dd a span{float:left;width:25px;height:22px;color:#404040;overflow:hidden;text-decoration:none;background-image:url(special-day.png);background-position:-999px -999px;background-repeat:no-repeat;margin:1px 0 0 1px;outline:none;}
    .calendar dd a:hover,.calendar dd a.selected{color:#FFF;background-color:#5792DC;}
    .calendar dd a.disabled,.calendar dd a.disabled:hover{color:#DCDCDC;cursor:default;background-color:transparent;}
    .calendar dd a span{display:block;width:25px;height:22px;margin:0;cursor:pointer;}
    .cal-prev,.cal-next,.cal-close{position:absolute;cursor:pointer;overflow:hidden;text-indent:-9999px;background:url(ico.png) no-repeat;}
    .cal-prev,.cal-next{top:50%;width:20px;height:38px;margin-top:-19px;}
    .cal-prev{left:10px;background-position:0 -25px;}
    .cal-next{right:10px;background-position:-20px -25px;}
    .cal-close{top:8px;right:8px;width:17px;height:17px;background-position:-40px -25px;}
    .calendar dd a.yuandan,
    .calendar dd a.chuxi,
    .calendar dd a.chunjie,
    .calendar dd a.yuanxiao,
    .calendar dd a.qingming,
    .calendar dd a.wuyi,
    .calendar dd a.duanwu,
    .calendar dd a.zhongqiu,
    .calendar dd a.guoqing,
    .calendar dd a.today
    {text-indent:-999px;}
    .calendar dd a.yuandan{background-position:0 0;}
    .calendar dd a.chuxi{background-position:0 -30px;}
    .calendar dd a.chunjie{background-position:0 -60px;}
    .calendar dd a.yuanxiao{background-position:0 -90px;}
    .calendar dd a.qingming{background-position:0 -120px;}
    .calendar dd a.wuyi{background-position:0 -150px;}
    .calendar dd a.duanwu{background-position:0 -180px;}
    .calendar dd a.zhongqiu{background-position:0 -210px;}
    .calendar dd a.guoqing{background-position:0 -240px;}
    .calendar dd a.today{background-position:0 -270px;}
    .calendar dd a.yuandan:hover, .calendar dd a.yuandan .selected{background-position:-31px 0;}
    .calendar dd a.chuxi:hover, .calendar dd a.chuxi .selected{background-position:-31px -30px;}
    .calendar dd a.chunjie:hover, .calendar dd a.chunjie .selected{background-position:-31px -60px;}
    .calendar dd a.yuanxiao:hover, .calendar dd a.yuanxiao .selected{background-position:-31px -90px;}
    .calendar dd a.qingming:hover, .calendar dd a.qingming .selected{background-position:-31px -120px;}
    .calendar dd a.wuyi:hover, .calendar dd a.wuyi .selected{background-position:-31px -150px;}
    .calendar dd a.duanwu:hover, .calendar dd a.duanwu .selected{background-position:-31px -180px;}
    .calendar dd a.zhongqiu:hover, .calendar dd a.zhongqiu .selected{background-position:-31px -210px;}
    .calendar dd a.guoqing:hover, .calendar dd a.guoqing .selected{background-position:-31px -240px;}
    .calendar dd a.today:hover, .calendar dd a.today .selected{background-position:-31px -270px;}
  </style>
  <style>
    body{font:14px/1.5 Tahoma;}
    #set{overflow:hidden;zoom:1;line-height:2;padding:10px 0;}
    #set button,#set input,#set label{float:left;}
    #set button{font:12px/1.8 Arial;cursor:pointer;}
    #set input{border:2px solid #CCC;height:19px;padding:3px;font:700 16px Arial;}
    #set button,#set input{margin-right:10px;}
    #copyright{font-size:12px;float:left;clear:both;width:100%;color:#9A9A9A;text-align:center;padding:10px 0;border-top:2px solid #3485C0;margin:15px 0;}
    #copyright a{background:#9A9A9A;border-radius:10px 10px 10px 10px;color:#FFF;padding:2px 5px;text-decoration:none;}
  </style>
</head>
<body>
<div id="set">
  <button>隐藏日历</button>
  <button>隐藏功能按钮</button>
  <button>节假日特殊显示</button>
  <label>日历个数：</label><input id="Cal_Count" type="text" maxlength="1" style="width:50px;" value="5" /><button>确定</button>
</div>
<div id="copyright">如蒙转载请注明出处 <a href="http://js.fgm.cc">Fgm.cc</a> , By &mdash; Ferris, QQ:21314130</div>
<!--日历控件JS源码-->
<script>
  //日历控件构造函数
  function Calendar() {
    this.initialize.apply(this, arguments)
  }
  Calendar.prototype = {
    constructor: Calendar,
    //初始化
    initialize: function(config) {
      config = config || {};
      //日历对象列表
      this.aCal = [],
              //插入指定位置
              this.insertBefore = typeof config.insertBefore === "object" ? config.insertBefore : document.getElementById(config.insertBefore);
      //日历id
      this.id = config.id || "C_" + (+new Date());
      //日历内容容器
      this.container = document.createElement("div");
      this.container.id = this.id;
      //关闭按钮
      this.oClose = document.createElement("span");
      this.oClose.className = "cal-close";
      //左按钮
      this.oPrev = document.createElement("span");
      this.oPrev.className = "cal-prev";
      //右按钮
      this.oNext = document.createElement("span");
      this.oNext.className = "cal-next";
      //是否显示功能按钮
      this.bShowBtn = config.showBtn;
      //是否特殊显示节假日
      this.holiday = config.holiday;
      //日历个数
      this.count = config.count || 1;
      //指定年份
      this.Year = config.year || new Date().getFullYear();
      //指定月份
      this.Month = config.month || new Date().getMonth() + 1;
      //选择的日期
      this.selectDay = config.selectDay;
      //当天回调函数
      this.onToday = config.onToday || function() {};
      //日历创建完毕回调函数
      this.onComplete = config.onComplete || function() {};
      //选择日期回调函数
      this.onSelectDay = config.onSelectDay || function() {};
      //今天
      this.today = new Date().getFullYear() + "-" + this.format(new Date().getMonth() + 1) + "-" + this.format(new Date().getDate());
      //配置功能按钮
      this.setBtn(this.bShowBtn);
      //添加事件
      this.addEvent();
    },
    //创建日历算法
    _Draw: function(iYear, iMonth) {
      var oContainer = document.createElement("div"),
              oDl = document.createElement("dl"),
              oDd = document.createElement("dd"),
              oFrag = document.createDocumentFragment(),
              //计算当月第一天是星期几
              firstDay = new Date(iYear, iMonth - 1, 1).getDay(),
              //计算当月有多少天
              lastDay = new Date(iYear, iMonth, 0).getDate(),
              //日历头
              aTmp = [
                "<dt class=\"date\">"+ iYear + "年" + iMonth +"月</dt>",
                "<dt><strong>日</strong></dt>",
                "<dt>一</dt>",
                "<dt>二</dt>",
                "<dt>三</dt>",
                "<dt>四</dt>",
                "<dt>五</dt>",
                "<dt><strong>六</strong></dt>"
              ],
              arr = [],
              cur, oA, i, len, sValue, classIndex;
      //-------------------------------------------------------------
      for(i = 1; i <= firstDay; i++) arr.push(0);
      for(i = 1; i <= lastDay; i++) arr.push(i);
      while(arr.length) {
        for(i = 1, len = arr.length; i <= len; i++) {
          if(arr.length) {
            oA = document.createElement("a");
            sValue = arr.shift();
            if(!sValue) {
              oA.innerHTML = "&nbsp;";
              oA.className = "disabled";
            }
            else {
              oA["data-date"] = iYear + "-" + this.format(iMonth) + "-" + this.format(sValue);
              oA["data-week"] = this.getWeek(oA["data-date"]);
              oA.href = "javascript:;";
              oA.innerHTML = sValue;
              cur = new Date(iYear, iMonth - 1, sValue);
              //屏蔽今天以前的日期选择
              parseInt(oA["data-date"].replace(/-/g, "")) < parseInt(this.today.replace(/-/g, "")) && (oA.className = "disabled");
              //节假日处理
              if(this.holiday) for(var className in this.dayName) this.isHoliday(oA, className)
            }
          }
          oFrag.appendChild(oA)
        }
      }
      //插入相关元素
      oDd.appendChild(oFrag);
      oDl.innerHTML = aTmp.join("");
      oDl.appendChild(oDd);
      oContainer.className = "cal-container";
      oContainer.appendChild(oDl);
      //记录日历队列
      this.aCal.push(oContainer);
      //返回生成好的日历
      return oContainer
    },
    //创建日历
    create: function() {
      var year = this.Year,
              month = this.Month,
              i = 0;
      //----------------------------------------------------------------
      this.container.className = "calendar"; //※指定日历控件className
      //清空日历队列
      while(this.aCal[0]) this.container.removeChild(this.aCal.shift());
      //批量生成日历
      for(i = 0; i < this.count; i++) {
        year += (month + (i ? 1 : 0)) > 12 ? 1 : 0;
        month = (month + (i ? 1 : 0)) % 12 || 12;
        this.container.appendChild(this._Draw(year, month))
      }
      //将日历插入页面, 如果未指定插入位置则插入BODY
      (this.insertBefore ? this.insertBefore.parentNode : document.body).insertBefore(this.container, this.insertBefore);
      //日历生成完毕的回调方法
      this.onComplete();
      return this
    },
    //根据日期创建日历
    Draw: function(date) {
      this.Year = date.getFullYear();
      this.Month = date.getMonth() + 1;
      //重新创建日历
      this.create()
    },
    //当前月
    NowMonth: function() {
      this.Draw(new Date())
    },
    //下月
    NextMonth: function() {
      this.Draw(new Date(this.Year, this.Month + (this.count - 1), 1))
    },
    //上月
    PrevMonth: function() {
      this.Draw(new Date(this.Year, this.Month - (this.count + 1), 1))
    },
    //计算是否为节假日
    isHoliday: function(obj, className) {
      if(new RegExp(obj["data-date"]).test(this.Holidays()[className].join())) {
        obj.className = className;
        obj["data-week"] = this.dayName[className];
        obj.innerHTML = "<span>"+ obj.innerHTML +"</span>"
      }
    },
    //格式化数字, 不足两位补0
    format: function(str) {
      return str.toString().replace(/^(\d)$/, "0$1")
    },
    //显示日历
    show: function() {
      this.container.style.display = "block"
    },
    //隐藏日历
    hide: function() {
      this.container.style.display = "none"
    },
    //按钮设置(显示/隐藏)
    setBtn: function(boolean) {
      var obj = this.container;
      //如果按钮没有创建过并且设置为显示, 则创建按钮, 并添加已创建标记
      if(!this.mark && boolean) {
        obj.insertBefore(this.oClose, obj.firstChild);
        obj.insertBefore(this.oPrev, obj.firstChild);
        obj.insertBefore(this.oNext, obj.firstChild);
        //添加已创建标记
        this.mark = true
      }
      //如果按钮已经创建过, 则设置其显示/隐藏
      this.oClose.style.display = this.oPrev.style.display = this.oNext.style.display = boolean ? "block" : "none";
    },
    //添加事件
    addEvent: function() {
      var that = this,
              obj = this.container,
              handler = null;
      //CLICK事件代理
      handler = function(e) {
        e = e || event
        var oTarget = e.target || e.srcElement;
        switch(oTarget.className) {
          case "cal-close":
            that.hide();
            break;
          case "cal-prev":
            that.PrevMonth();
            break;
          case "cal-next":
            that.NextMonth();
            break;
        }
        if(oTarget.tagName.toUpperCase() === "A" && oTarget.className != "disabled") {
          that.onSelectDay(oTarget)
        }
        if(oTarget.tagName.toUpperCase() === "SPAN" && oTarget.parentNode.tagName.toUpperCase() === "A") {
          that.onSelectDay(oTarget.parentNode)
        }
      }
      //为日历控件添加CLICK事件监听
      if(obj.addEventListener)
        obj.addEventListener("click", handler, false);
      else if(obj.attachEvent)
        obj.attachEvent("onclick", handler)
    },
    //获取指定日期是星期几 @param date string yyyy-mm-dd
    getWeek: function(date) {
      var aWeek = ["\u65e5", "\u4e00", "\u4e8c", "\u4e09", "\u56db", "\u4e94", "\u516d"],
              arr = date.split(/-/g);
      return "\u661f\u671f" + aWeek[new Date(arr[0], arr[1] - 1, arr[2]).getDay()]
    },
    //节假日名字
    dayName: {
      "today":"\u4eca\u5929",
      "yuandan":"\u5143\u65e6",
      "chuxi":"\u9664\u5915",
      "chunjie":"\u6625\u8282",
      "yuanxiao":"\u5143\u5bb5\u8282",
      "qingming":"\u6e05\u660e",
      "wuyi":"\u52b3\u52a8\u8282",
      "duanwu":"\u7aef\u5348\u8282",
      "zhongqiu":"\u4e2d\u79cb\u8282",
      "guoqing":"\u56fd\u5e86\u8282"
    },
    //2012——2020年节假日数据
    Holidays: function(){
      return {
        today: [this.today],
        yuandan: ["2012-01-01", "2013-01-01", "2014-01-01", "2015-01-01", "2016-01-01", "2017-01-01", "2018-01-01", "2019-01-01", "2020-01-01"],
        chuxi: ["2012-01-22", "2013-02-09", "2014-01-30", "2015-02-18", "2016-02-07", "2017-01-27", "2018-02-15", "2019-02-04", "2020-01-24"],
        chunjie: ["2012-01-23", "2013-02-10", "2014-01-31", "2015-02-19", "2016-02-08", "2017-01-28", "2018-02-16", "2019-02-05", "2020-01-25"],
        yuanxiao: ["2012-02-06", "2013-02-24", "2014-2-14", "2015-03-05", "2016-02-22", "2017-02-11", "2018-03-02", "2019-02-19", "2020-02-8"],
        qingming: ["2012-04-04", "2013-04-04", "2014-04-05", "2015-04-05", "2016-04-04", "2017-04-04", "2018-04-05", "2019-04-05", "2020-04-04"],
        wuyi: ["2012-05-01", "2013-05-01", "2014-05-01", "2015-05-01", "2016-05-01", "2017-05-01", "2018-05-01", "2019-05-01", "2020-05-01"],
        duanwu: ["2012-06-23", "2013-06-12", "2014-06-02", "2015-06-20", "2016-06-09", "2017-05-30", "2018-06-18", "2019-06-07", "2020-06-25"],
        zhongqiu: ["2012-09-30", "2013-09-19", "2014-09-08", "2015-09-27", "2016-09-15", "2017-10-04", "2018-09-24", "2019-09-13", "2020-10-01"],
        guoqing: ["2012-10-01", "2013-10-01", "2014-10-01", "2015-10-01", "2016-10-01", "2017-10-01", "2018-10-01", "2019-10-01", "2020-10-01"]
      }
    }
  };
</script>
<!--控件应用实例-->
<script>
  //公用方法
  var _ = {
            $: function(id) {
              return typeof id === "object" ? id : document.getElementById(id)
            },
            $$: function(tagName, oParent) {
              return (oParent || document).getElementsByTagName(tagName)
            },
            hasClass: function(element, className) {
              return new RegExp("(^|\\s)" + className + "(\\s|$)").test(element.className)
            },
            addClass: function(element, className) {
              var arr = element.className.split(/\s+/);
              this.hasClass(element, className) || arr.push(className);
              element.className = arr.join(" ").replace(/(^\s*)|(\s*$)/, "")
            },
            removeClass: function(element, className) {
              element.className = element.className.replace(new RegExp("(^|\\s)" + className + "(\\s|$)", "g"), "").split(/\s+/).join(" ")
            }
          },
//获取按钮
          aInput = _.$$("input"),
//创建一个日历实例
          oCalendar = new Calendar({
            count:3,					//显示日历个数
            showBtn: !0,				//显示功能按钮
            onSelectDay: fnSelectDay,	//选择日期回函函数
            insertBefore: "copyright"	//将日历插入到id为copyright元素前, 如果设置此项, 默认插入BODY
          });
  //将日历插入页面
  oCalendar.create();
  //回调函数
  function fnSelectDay(obj) {
    var aA = oCalendar.container.getElementsByTagName("a"),
            len = aA.length;
    for(;len--;) _.removeClass(aA[len].children[0] ? aA[len].children[0] : aA[len], "selected");
    _.addClass(obj.children[0] ? obj.children[0] : obj, "selected");
    alert(obj["data-date"] + "\u3010"+ obj["data-week"] +"\u3011\n")
  };
  //INPUT事件代理
  document.onclick = function(e) {
    e = e || event;
    var oTarget = e.target || e.srcElement;
    if(oTarget.tagName.toUpperCase() == "BUTTON") {
      switch(oTarget.innerHTML) {
        case "\u663e\u793a\u529f\u80fd\u6309\u94ae":
          oCalendar.setBtn(true);
          oTarget.innerHTML = "\u9690\u85cf\u529f\u80fd\u6309\u94ae";
          break;
        case "\u9690\u85cf\u529f\u80fd\u6309\u94ae":
          oCalendar.setBtn(false);
          oTarget.innerHTML = "\u663e\u793a\u529f\u80fd\u6309\u94ae";
          break;
        case "\u663e\u793a\u65e5\u5386":
          oCalendar.show();
          oTarget.innerHTML = "\u9690\u85cf\u65e5\u5386";
          break;
        case "\u9690\u85cf\u65e5\u5386":
          oCalendar.hide();
          oTarget.innerHTML = "\u663e\u793a\u65e5\u5386"
          break;
        case "\u8282\u5047\u65e5\u7279\u6b8a\u663e\u793a":
          oCalendar.holiday = !0;
          oCalendar.Draw(new Date);
          oTarget.innerHTML = "\u8282\u5047\u65e5\u6b63\u5e38\u663e\u793a"
          break;
        case "\u8282\u5047\u65e5\u6b63\u5e38\u663e\u793a":
          oCalendar.holiday = !1;
          oCalendar.Draw(new Date);
          oTarget.innerHTML = "\u8282\u5047\u65e5\u7279\u6b8a\u663e\u793a"
          break;
        case "\u786e\u5b9a":
          var count = _.$("Cal_Count");
          if(/^[^1-5]$/.test(count.value || 0)) {
            count.select();
            alert("\u9519\u8bef\u63d0\u793a\uff1a\n\n\u53ea\u80fd\u8f93\u5165\u6570\u5b57[1-5]")
          }
          else {
            oCalendar.count = parseInt(count.value);
            oCalendar.Draw(new Date())
          }
          break;
      }
    }
  };
</script>
</body>
</html>